#include <QThread>
#include "Client.h"
#include "Const.h"

QString Client::sUsername; // 用户名

/**
 * 构造方法。
 */
Client::Client() :
    mFriendCount(0),
    mIconIndex(-1) {
    // 连接信号和槽
    QTcpSocket::connect(this, SIGNAL(readyRead()), SLOT(onReceive()));

    // 连接服务器
    QTcpSocket::connectToHost(ImProtocol::SERVER_IP, ImProtocol::SERVER_PORT);
    bool result = QTcpSocket::waitForConnected(Const::SERVER_CONNECT_TIME_OUT);
    if (!result) {
        throw static_cast<int>(QTcpSocket::error());
    }
}

/**
 * 析构方法。
 */
Client::~Client() {
    // 离线
    this->logoff();

    // 延迟一下，防止数据还没发送完成就关闭网络
    QThread::msleep(100);

    // 断开连接
    QTcpSocket::disconnectFromHost();

    // 关闭
    QTcpSocket::close();
}

/**
 * 添加好友。
 * @param friendName    好友名字
 * @return 结果
 */
ResultCode Client::addFriend(const char* friendName) {
    // 检查是否超过好友上限
    if (mFriendCount > ImProtocol::MAX_USER_COUNT) {
        return TOO_MANY_FRIENDS;
    }

    // 检查好友名
    if (friendName == sUsername) {
        return FRIEND_NAME_EQUAL_SELF_NAME;
    }

    // 发送添加好友请求
    AddFriendRequest request(friendName, mIconIndex, sUsername.toStdString().c_str());
    this->sendData(&request, sizeof(request));

    return SUCCEEDED;
}

/**
 * 允许或拒绝添加好友。
 * @param allow     允许则为 true，否则为 false
 * @param request   添加好友请求
 */
void Client::allowAddFriend(bool allow, AddFriendRequest* request) {
    // 发送结果
    AddFriendResult addFriendResult(mIconIndex, request->mDstUsername,
        allow ? ADD_FRIEND_ALLOW: ADD_FRIEND_NOT_ALLOW, request->mSrcUsername);
    this->sendData(&addFriendResult, sizeof(addFriendResult));

    // 如果允许，则好友数加1
    if (allow) {
        mFriendCount++;
    }
}

/**
 * 聊天。
 * @param chatInfo  聊天信息
 */
void Client::chat(ChatInfo* chatInfo) {
    this->sendData(chatInfo, sizeof(ChatInfo));
}

/**
 * 获取好友数。
 * @return 好友数
 */
int Client::getFriendCount() {
    return mFriendCount;
}

/**
 * 获取自己的名字。
 * @return 自己的名字
 */
const char* Client::getUsername() {
    return sUsername.toStdString().c_str();
}

/**
 * 处理添加好友请求。
 * @param request   添加好友请求
 */
void Client::handleAddFriendRequest(AddFriendRequest* request) {
    emit UM_GET_ADD_FRIEND_REQUEST(request);
}

/**
 * 处理添加好友结果。
 * @param result    添加好友结果
 */
void Client::handleAddFriendResult(AddFriendResult* result) {
    if (result->mResultCode == ADD_FRIEND_ALLOW && result->mDstIconIndex != -1) {
        mFriendCount++;
    }

    emit UM_GET_ADD_FRIEND_RESULT(result);
}

/**
 * 处理聊天。
 * @param chatInfo  聊天信息
 */
void Client::handleChat(ChatInfo* chatInfo) {
    emit UM_CHAT_S2C(chatInfo);
}

/**
 * 处理其他用户的离线请求。
 * @param request   其他用户的离线请求
 */
void Client::handleFriendLogoff(LogoffRequest* request) {
    emit UM_FRIEND_LOGOFF(request);
}

/**
 * 处理其他用户的登录消息。
 * @param friendLogon   其他用户的登录消息
 */
void Client::handleFriendLogon(FriendLogon* friendLogon) {
    emit UM_FRIEND_LOGON(friendLogon);
}

/**
 * 处理登录结果。
 * @param result    登录结果
 */
void Client::handleLogonResult(LogonResult* result) {
    emit UM_GET_LOGON_RESULT(result);

    if (result->mResultCode == LOGON_SUCCEEDED) {
        mIconIndex = result->mIconIndex;
        mFriendCount = result->mFriendCount;
    } else {
        sUsername.clear();
    }
}

/**
 * 处理注册结果。
 * @param result    注册结果
 */
void Client::handleRegResult(RegResult* result) {
    emit UM_GET_REG_RESULT(result);

    if (result->mResultCode != REG_SUCCEEDED) {
        sUsername.clear();
    }
}

/**
 * 离线。
 * @return 结果
 */
void Client::logoff() {
    if (sUsername.isEmpty()) {
        return;
    }

    LogoffRequest request(sUsername.toStdString().c_str());
    this->sendData(&request, sizeof(request));

    sUsername.clear();
}

/**
 * 登录。
 * @param username  用户名
 * @param password  密码
 * @return 结果
 */
void Client::logon(const char* username, const char* password) {
    // 发送登录请求
    LogonRequest request(password, username);
    this->sendData(&request, sizeof(request));

    // 保存用户名
    sUsername = username;
}

/**
 * 处理服务器发送来的数据。
 * @param pro  协议
 */
void Client::onGetMessageFromServer(ImProtocol* pro) {
    switch (pro->mType) {
        case ImProtocol::ADD_FRIEND_REQUEST:
            this->handleAddFriendRequest(reinterpret_cast<AddFriendRequest*>(pro));
            break;
        case ImProtocol::ADD_FRIEND_RESULT:
            this->handleAddFriendResult(reinterpret_cast<AddFriendResult*>(pro));
            break;
        case ImProtocol::CHAT:
            this->handleChat(reinterpret_cast<ChatInfo*>(pro));
            break;
        case ImProtocol::FRIEND_LOGON:
            this->handleFriendLogon(reinterpret_cast<FriendLogon*>(pro));
            break;
        case ImProtocol::LOGOFF:
            this->handleFriendLogoff(reinterpret_cast<LogoffRequest*>(pro));
            break;
        case ImProtocol::LOGON:
            this->handleLogonResult(reinterpret_cast<LogonResult*>(pro));
            break;
        case ImProtocol::REG:
            this->handleRegResult(reinterpret_cast<RegResult*>(pro));
            break;
    }
}

/**
 * 接收到数据的响应方法。
 */
void Client::onReceive() {
    // 接收数据长度
    qint64 dataSize= 0;
    qint64 read = QTcpSocket::read(reinterpret_cast<char*>(&dataSize), sizeof(dataSize));
    if (read <= 0) {
        return;
    }

    // 接收数据
    char* buffer = new char[static_cast<quint64>(dataSize)];
    read = QTcpSocket::read(buffer, dataSize);
    if (read <= 0) {
        delete[] buffer;
        return;
    }

    // 将数据转换成协议
    ImProtocol* pro = reinterpret_cast<ImProtocol*>(buffer);

    // 处理协议
    this->onGetMessageFromServer(pro);

    // 释放缓存
    delete[] buffer;
}

/**
 * 注册。
 * @param username  用户名
 * @param password  密码
 * @param iconIndex 头像索引
 */
void Client::reg(const char* username, const char* password, int iconIndex) {
    // 发送注册请求
    RegRequest request(iconIndex, password, username);
    this->sendData(&request, sizeof(request));

    // 保存用户名和头像索引
    sUsername = username;
    mIconIndex = iconIndex;
}

/**
 * 发送数据。
 * @param pro       协议
 * @param dataSize  数据长度
 */
void Client::sendData(ImProtocol* pro, qint64 dataSize) {
    // 发送数据长度
    QTcpSocket::write(reinterpret_cast<const char*>(&dataSize), sizeof(dataSize));

    // 发送数据
    QTcpSocket::write(reinterpret_cast<char*>(pro), dataSize);
    QTcpSocket::flush();

    // 防止发送过快导致服务器无法响应
    QThread::msleep(100);
}
